FunC Yemek Kitabı
FunC Yemek Kitabı'nın oluşturulmasının temel nedeni, FunC geliştiricilerinin tüm deneyimlerini bir araya getirerek gelecekteki geliştiricilerin bunu kullanabilmelerini sağlamaktır!
FunC Dokümantasyonu
ile karşılaştırıldığında, bu makale her FunC geliştiricisinin akıllı sözleşmelerin geliştirilmesi sırasında çözdüğü günlük görevlere daha fazla odaklanmaktadır.
Temeller
Bir if ifadesi nasıl yazılır
Herhangi bir olayın ilgili olup olmadığını kontrol etmek istediğimizi varsayalım. Bunu yapmak için bayrak değişkenini kullanıyoruz. FunC'de true
değerinin -1
ve false
değerinin 0
olduğunu unutmayın.
int flag = 0; ;; yanlış
if (flag) {
;; bir şey yap
}
else {
;; işlemi reddet
}
💡 Not: Kullanıcı
==
operatörüne ihtiyacımız yok, çünkü0
değerifalse
olduğu için diğer herhangi bir değertrue
olur.
💡 Yararlı bağlantılar
"If ifadesi" belgelerde
Bir repeat döngüsü nasıl yazılır
Örnek olarak üslü sayma işlemini alabiliriz.
int number = 2;
int multiplier = number;
int degree = 5;
repeat(degree - 1) {
number *= multiplier;
}
💡 Yararlı bağlantılar
"Repeat döngüsü" belgelerde
Bir while döngüsü nasıl yazılır
Belirli bir eylemi ne sıklıkla gerçekleştireceğimizi bilmediğimizde while kullanışlıdır. Örneğin, dört referansa kadar diğer hücreleri depolayabilen bir cell
alalım.
cell inner_cell = begin_cell() ;; yeni bir boş oluşturucu oluştur
.store_uint(123, 16) ;; değeri 123 ve uzunluğu 16 bit olan uint depola
.end_cell(); ;; oluşturucuyu bir hücreye dönüştür
cell message = begin_cell()
.store_ref(inner_cell) ;; hücreyi referans olarak depola
.store_ref(inner_cell)
.end_cell();
slice msg = message.begin_parse(); ;; hücreyi dilime dönüştür
while (msg.slice_refs_empty?() != -1) { ;; -1'in true olduğunu hatırlatmalıyız
cell inner_cell = msg~load_ref(); ;; dilim msg'den hücreyi yükle
;; bir şey yap
}
💡 Yararlı bağlantılar
"While döngüsü" belgelerde
"Cell" belgelerde
"slice_refs_empty?()" belgelerde
"store_ref()" belgelerde
"begin_cell()" belgelerde
"end_cell()" belgelerde
"begin_parse()" belgelerde
Bir do until döngüsü nasıl yazılır
Döngünün en az bir kez çalışmasını gerektiğinde do until
kullanıyoruz.
int flag = 0;
do {
;; bayrak yanlış (0) olduğu için bile bir şey yap
} until (flag == -1); ;; -1 true
💡 Yararlı bağlantılar
"Until döngüsü" belgelerde
Dilimin boş olup olmadığını nasıl belirleriz
slice
ile çalışmadan önce, doğru bir şekilde işleyebilmesi için içinde veri olup olmadığını kontrol etmemiz gerekir. Bunu yapmak için slice_empty?()
kullanabiliriz, ancak en az bir bit
veri veya bir ref
varsa 0
(false
) döneceğini dikkate almalıyız.
;; boş dilim oluşturma
slice empty_slice = "";
;; `slice_empty?()` `true` döner, çünkü dilimde herhangi bir `bit` veya `ref` yoktur
empty_slice.slice_empty?();
;; yalnızca bit içeren dilim oluşturma
slice slice_with_bits_only = "Merhaba, dünya!";
;; `slice_empty?()` `false` döner, çünkü dilimde herhangi bir `bit` bulunur
slice_with_bits_only.slice_empty?();
;; yalnızca ref içeren dilim oluşturma
slice slice_with_refs_only = begin_cell()
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_empty?()` `false` döner, çünkü dilimde herhangi bir `ref` bulunur
slice_with_refs_only.slice_empty?();
;; hem bit hem de ref içeren dilim oluşturma
slice slice_with_bits_and_refs = begin_cell()
.store_slice("Merhaba, dünya!")
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_empty?()` `false` döner, çünkü dilimde herhangi bir `bit` ve `ref` bulunur
slice_with_bits_and_refs.slice_empty?();
💡 Yararlı bağlantılar
"slice_empty?()" belgelerde
"store_slice()" belgelerde
"store_ref()" belgelerde
"begin_cell()" belgelerde
"end_cell()" belgelerde
"begin_parse()" belgelerde
Sadece bits
olmadığını (ama refs
olabilir) nasıl belirleriz
Eğer yalnızca bits
kontrol etmemiz gerekiyorsa ve dilimde herhangi bir refs
olup olmadığı önemli değilse, slice_data_empty?()
kullanmalıyız.
;; boş dilim oluşturma
slice empty_slice = "";
;; `slice_data_empty?()` `true` döner, çünkü dilimde herhangi bir `bit` yoktur
empty_slice.slice_data_empty?();
;; yalnızca bit içeren dilim oluşturma
slice slice_with_bits_only = "Merhaba, dünya!";
;; `slice_data_empty?()` `false` döner, çünkü dilimde herhangi bir `bit` bulunur
slice_with_bits_only.slice_data_empty?();
;; yalnızca ref içeren dilim oluşturma
slice slice_with_refs_only = begin_cell()
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_data_empty?()` `true` döner, çünkü dilimde herhangi bir `bit` yoktur
slice_with_refs_only.slice_data_empty?();
;; hem bit hem de ref içeren dilim oluşturma
slice slice_with_bits_and_refs = begin_cell()
.store_slice("Merhaba, dünya!")
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_data_empty?()` `false` döner, çünkü dilimde herhangi bir `bit` bulunur
slice_with_bits_and_refs.slice_data_empty?();
💡 Yararlı bağlantılar
"slice_data_empty?()" belgelerde
"store_slice()" belgelerde
"store_ref()" belgelerde
"begin_cell()" belgelerde
"end_cell()" belgelerde
"begin_parse()" belgelerde
Sadece refs
olmadığını (ama bits
olabilir) nasıl belirleriz
Eğer yalnızca refs
ile ilgileniyorsak, varlıklarını slice_refs_empty?()
kullanarak kontrol etmeliyiz.
;; boş dilim oluşturma
slice empty_slice = "";
;; `slice_refs_empty?()` `true` döner, çünkü dilimde herhangi bir `ref` yoktur
empty_slice.slice_refs_empty?();
;; yalnızca bit içeren dilim oluşturma
slice slice_with_bits_only = "Merhaba, dünya!";
;; `slice_refs_empty?()` `true` döner, çünkü dilimde herhangi bir `ref` yoktur
slice_with_bits_only.slice_refs_empty?();
;; yalnızca ref içeren dilim oluşturma
slice slice_with_refs_only = begin_cell()
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_refs_empty?()` `false` döner, çünkü dilimde herhangi bir `ref` bulunur
slice_with_refs_only.slice_refs_empty?();
;; hem bit hem de ref içeren dilim oluşturma
slice slice_with_bits_and_refs = begin_cell()
.store_slice("Merhaba, dünya!")
.store_ref(null())
.end_cell()
.begin_parse();
;; `slice_refs_empty?()` `false` döner, çünkü dilimde herhangi bir `ref` bulunur
slice_with_bits_and_refs.slice_refs_empty?();
💡 Yararlı bağlantılar
"slice_refs_empty?()" belgelerde
"store_slice()" belgelerde
"store_ref()" belgelerde
"begin_cell()" belgelerde
"end_cell()" belgelerde
"begin_parse()" belgelerde
Bir hücrenin boş olup olmadığını nasıl belirleriz
Bir cell
içinde herhangi bir veri olup olmadığını kontrol etmek için önce onu bir slice
'e dönüştürmeliyiz. Sadece bits
ile ilgilendiğimizde slice_data_empty?()
, yalnızca refs
ile ilgilendiğimizde slice_refs_empty?()
kullanmalıyız. Herhangi bir verinin var olup olmadığını kontrol etmek istediğimizde slice_empty?()
kullanmalıyız.
cell cell_with_bits_and_refs = begin_cell()
.store_uint(1337, 16)
.store_ref(null())
.end_cell();
;; `cell` türünü `begin_parse()` ile dilime değiştirin
slice cs = cell_with_bits_and_refs.begin_parse();
;; dilimin boş olup olmadığını belirle
if (cs.slice_empty?()) {
;; hücre boş
}
else {
;; hücre boş değil
}
💡 Yararlı bağlantılar
"slice_empty?()" belgelerde
"begin_cell()" belgelerde
"store_uint()" belgelerde
"end_cell()" belgelerde
"begin_parse()" belgelerde
Bir dict'in boş olup olmadığını nasıl belirleriz
Dict içinde veri varlığını kontrol etmek için dict_empty?()
yöntemini kullanırız. Bu yöntem genellikle bir null
hücresinin boş bir sözlük olduğunu düşünüldüğünde cell_null?()
ile eşdeğerdir.
cell d = new_dict();
d~udict_set(256, 0, "merhaba");
d~udict_set(256, 1, "dünya");
if (d.dict_empty?()) { ;; Sözlüğün boş olup olmadığını belirleyin
;; sözlük boş
}
else {
;; sözlük boş değil
}
💡 Yararlı bağlantılar
"dict_empty?()" belgelerde
"new_dict()" belgelerde
boş bir sözlük oluşturma
"dict_set()" belgelerde
sözlük d'ye bazı öğeler ekleyerek, böylece boş değildir
Bir tuple'ın boş olup olmadığını nasıl belirleriz
tuples
ile çalışırken, içlerinde herhangi bir değerin olup olmadığını her zaman bilmek önemlidir. Eğer boş bir tuple
'dan değer çıkarmaya çalışırsak, "geçerli boyutta bir tuple değil" hatasını alırız ve exit code 7
döner.
;; tlen fonksiyonunu tanımlayın çünkü stdlib'de mevcut değildir
(int) tlen (tuple t) asm "TLEN";
() main () {
tuple t = empty_tuple();
t~tpush(13);
t~tpush(37);
if (t.tlen() == 0) {
;; tuple boş
}
else {
;; tuple boş değil
}
}
💡 Not: tlen assembly fonksiyonunu tanımlıyoruz. Daha fazlasını okuyabilirsiniz
burada
vetüm assembly komutları listesine
göz atabilirsiniz.
💡 Yararlı bağlantılar
"empty_tuple?()" belgelerde
"tpush()" belgelerde
"Exit kodları" belgelerde
LISP tarzı bir listenin boş olup olmadığını nasıl belirleriz
tuple numbers = null();
numbers = cons(100, numbers);
if (numbers.null?()) {
;; liste tarzı liste boş
} else {
;; liste tarzı liste boş değil
}
Liste tarzı listemize cons
fonksiyonu ile 100 sayısını ekliyoruz, böylece boş olmayacaktır.
Sözleşmenin durumunun boş olup olmadığını nasıl belirleriz
Bir counter
'ımız olduğunu varsayalım, bu değişken işlemlerin sayısını depolar. Bu değişken akıllı sözleşme durumunda ilk işlem sırasında mevcut olmayacağı için, böyle bir durumu işlememiz gerekmektedir. Durum boşsa, bir counter
değişkeni oluşturur ve kaydederiz.
;; `get_data()` sözleşme durumundan veri hücresini döndürecektir
cell contract_data = get_data();
slice cs = contract_data.begin_parse();
if (cs.slice_empty?()) {
;; sözleşme verisi boş, bu yüzden sayacı oluşturup kaydediyoruz
int counter = 1;
;; hücre oluştur, sayacı ekle ve sözleşme durumuna kaydet
set_data(begin_cell().store_uint(counter, 32).end_cell());
}
else {
;; sözleşme verisi boş değil, bu yüzden sayacımızı alıp artırıyoruz ve kaydediyoruz
;; sayacımızın bit cinsinden doğru uzunluğunu belirtmeliyiz
int counter = cs~load_uint(32) + 1;
set_data(begin_cell().store_uint(counter, 32).end_cell());
}
💡 Not: Bir sözleşmenin durumunun boş olduğunu,
cell boşsa
belirleyerek anlayabiliriz.
💡 Yararlı bağlantılar
"get_data()" belgelerde
"begin_parse()" belgelerde
"slice_empty?()" belgelerde
"set_data?()" belgelerde
İç mesaj hücresinin nasıl oluşturulacağı
Eğer sözleşmenin bir iç mesaj göndermesini istiyorsak, öncelikle teknik bayrakları, alıcı adresini ve diğer verileri belirterek doğru bir şekilde bir hücre oluşturmalıyız.
;; Geçerli adresi bir dize içinden almak için `a` literal'ını kullanıyoruz
slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a;
int amount = 1000000000;
;; işlemleri tanımlamak için `op` kullanıyoruz
int op = 0;
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_coins(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; varsayılan mesaj başlıkları (mesaj gönderme sayfasına bakınız)
.store_uint(op, 32)
.end_cell();
send_raw_message(msg, 3); ;; mod 3 - ücretleri ayrı öde ve hataları göz ardı et
💡 Not: Bu örnekte geçerli adresi almak için
a
literal'ını kullanıyoruz. Dize literal'ları hakkında daha fazla bilgiyebelgelerde
ulaşabilirsiniz.
💡 Not: Daha fazlasını
belgelerde
bulabilirsiniz. Ayrıca bu bağlantıyladüzen
sayfasına gidebilirsiniz.
💡 Yararlı bağlantılar
"begin_cell()" belgelerde
"store_uint()" belgelerde
"store_slice()" belgelerde
"store_coins()" belgelerde
"end_cell()" belgelerde
"send_raw_message()" belgelerde
Bir mesaj hücresinin gövdesini referans olarak nasıl içerebiliriz
Mesajın gövdesinde bayraklar ve diğer teknik verilerden sonra int
, slice
ve cell
gönderebiliriz. İkincisi durumunda, store_ref()
öncesinde bit 1
'e ayarlanmalıdır, bu da cell
'in devam edeceğini belirtir.
Mesaj gövdesini, yeterli alan olduğundan eminsek, aynı cell
içinde başlıkla birlikte gönderebiliriz. Bu durumda, bit 0
'a ayarlanmalıdır.
;; Geçerli adresi bir dize içinden almak için `a` literal'ını kullanıyoruz
slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a;
int amount = 1000000000;
int op = 0;
cell message_body = begin_cell() ;; mesaj ile birlikte bir hücre oluşturma
.store_uint(op, 32)
.store_slice("❤")
.end_cell();
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_coins(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) ;; varsayılan mesaj başlıkları (mesaj gönderme sayfasına bakınız)
.store_uint(1, 1) ;; hücre gideceğini belirtmek için biti 1'e ayarla
.store_ref(message_body)
.end_cell();
send_raw_message(msg, 3); ;; mod 3 - ücretleri ayrı öde ve hataları göz ardı et
💡 Not: Bu örnekte geçerli adresi almak için
a
literal'ını kullanıyoruz. Dize literal'ları hakkında daha fazla bilgiyebelgelerde
ulaşabilirsiniz.
💡 Not: Bu örnekte, gelen tonları alıp, belirtilen kadar (miktar) göndermek için mod 3 kullanıldı. Komisyonu sözleşme bakiyesinden ödeyerek hataları göz ardı ediyor. Mod 64, alınan tüm tonların, komisyon çıkarılarak geri döndürülmesi için gereklidir ve mod 128, tüm bakiyeyi gönderecektir.
💡 Not:
Bir mesaj oluşturuyoruz
, ancak mesaj gövdesini ayrı olarak ekliyoruz.
💡 Yararlı bağlantılar
"begin_cell()" belgelerde
"store_uint()" belgelerde
"store_slice()" belgelerde
"store_coins()" belgelerde
"end_cell()" belgelerde
"send_raw_message()" belgelerde
Bir mesaj hücresinin gövdesini slice olarak nasıl içerebiliriz
Mesaj gönderirken, mesajın gövdesi ya cell
ya da slice
olarak gönderilebilir. Bu örnekte, mesajın gövdesini slice
içinde gönderiyoruz.
;; Geçerli adresi bir dize içinden almak için `a` literal'ını kullanıyoruz
slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a;
int amount = 1000000000;
int op = 0;
slice message_body = "❤";
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_coins(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; varsayılan mesaj başlıkları (mesaj gönderme sayfasına bakınız)
.store_uint(op, 32)
.store_slice(message_body)
.end_cell();
send_raw_message(msg, 3); ;; mod 3 - ücretleri ayrı öde ve hataları göz ardı et
💡 Not: Bu örnekte geçerli adresi almak için
a
literal'ını kullanıyoruz. Dize literal'ları hakkında daha fazla bilgiyebelgelerde
ulaşabilirsiniz.
💡 Not: Bu örnekte, gelen tonları alıp, belirtilen kadar (miktar) göndermek için mod 3 kullanıldı. Komisyonu sözleşme bakiyesinden ödeyerek hataları göz ardı ediyor. Mod 64, alınan tüm tonların, komisyon çıkarılarak geri döndürülmesi için gereklidir ve mod 128, tüm bakiyeyi gönderecektir.
💡 Not:
Bir mesaj oluşturuyoruz
, ancak mesajı bir dilim olarak gönderiyoruz.
İkili demetler üzerinde nasıl yineleme yapılır (her iki yönde)
Bir dizi veya yığın ile çalışmak istersek, FunC'te demet gerekli olacaktır. İlk olarak, bunlarla çalışabilmek için değerleri yineleyebilmemiz gerekiyor.
(int) tlen (tuple t) asm "TLEN";
forall X -> (tuple) to_tuple (X x) asm "NOP";
() main () {
tuple t = to_tuple([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
int len = t.tlen();
int i = 0;
while (i < len) {
int x = t.at(i);
;; x ile bir şeyler yap
i = i + 1;
}
i = len - 1;
while (i >= 0) {
int x = t.at(i);
;; x ile bir şeyler yap
i = i - 1;
}
}
💡 Not
tlen
assembly fonksiyonunu tanımlıyoruz. Daha fazla bilgi almak için buradan okuyabilir ve tüm montaj komutlarının listesini görebilirsiniz.
Ayrıca
to_tuple
fonksiyonunu da tanımlıyoruz. Bu sadece herhangi bir girişin veri tipini demete dönüştürür, bu nedenle kullanırken dikkatli olun.
asm
anahtar kelimesi kullanarak kendi fonksiyonlarınızı nasıl yazarsınız
Herhangi bir özellik kullanırken aslında bizim için önceden hazırlanmış yöntemleri stdlib.fc
içinde kullanıyoruz. Ancak gerçekte, elimizde daha fazla fırsat bulunmaktadır ve bunları kendimiz yazmayı öğrenmemiz gerekiyor.
Örneğin, tuple
'a bir eleman ekleyen tpush
adlı bir metodumuz var, ancak tpop
yok. Bu durumda, şunu yapmalıyız:
;; ~ demek değiştirme metodudur
forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP";
Döngü için tuple
uzunluğunu öğrenmek istiyorsak, TLEN
asm talimatıyla yeni bir fonksiyon yazmalıyız:
int tuple_length (tuple t) asm "TLEN";
stdlib.fc
'den zaten bildiğimiz bazı fonksiyon örnekleri:
slice begin_parse(cell c) asm "CTOS";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
💡 Yararlı bağlantılar:
"değiştirme metodları" belgelerinde
"stdlib" belgelerinde
"TVM talimatları" belgelerinde
N-nestel demetler üzerinde yineleme
Bazen iç içe demetleri yinelemek isteriz. Aşağıdaki örnek, bellekleri baştan başlayarak [[2,6],[1,[3,[3,5]]], 3]
biçimindeki bir demet içindeki tüm öğeleri yineleyecektir.
int tuple_length (tuple t) asm "TLEN";
forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP";
forall X -> int is_tuple (X x) asm "ISTUPLE";
forall X -> tuple cast_to_tuple (X x) asm "NOP";
forall X -> int cast_to_int (X x) asm "NOP";
forall X -> (tuple) to_tuple (X x) asm "NOP";
;; global değişken tanımla
global int max_value;
() iterate_tuple (tuple t) impure {
repeat (t.tuple_length()) {
var value = t~tpop();
if (is_tuple(value)) {
tuple tuple_value = cast_to_tuple(value);
iterate_tuple(tuple_value);
}
else {
if(value > max_value) {
max_value = value;
}
}
}
}
() main () {
tuple t = to_tuple([[2,6], [1, [3, [3, 5]]], 3]);
int len = t.tuple_length();
max_value = 0; ;; max_value'yi sıfırla;
iterate_tuple(t); ;; demeti yinele ve maksimum değeri bul
~dump(max_value); ;; 6
}
💡 Yararlı bağlantılar
"Global değişkenler" belgelerinde
"~dump" belgelerinde
"TVM talimatları" belgelerinde
Demetlerle temel işlemler
(int) tlen (tuple t) asm "TLEN";
forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP";
() main () {
;; boş bir demet oluşturma
tuple names = empty_tuple();
;; yeni öğeler ekle
names~tpush("Naito Narihira");
names~tpush("Shiraki Shinichi");
names~tpush("Akamatsu Hachemon");
names~tpush("Takaki Yuichi");
;; son öğeyi çıkar
slice last_name = names~tpop();
;; ilk öğeyi al
slice first_name = names.first();
;; indeks ile bir öğe al
slice best_name = names.at(2);
;; liste uzunluğunu al
int number_names = names.tlen();
}
X tipini çözme
Aşağıdaki örnek, bir değerin bir demette bulunup bulunmadığını kontrol eder, ancak demet değerleri X (cell, slice, int, tuple, int) içerir. Değeri kontrol etmemiz ve uygun şekilde dönüştürmemiz gerekiyor.
forall X -> int is_null (X x) asm "ISNULL";
forall X -> int is_int (X x) asm "<{ TRY:<{ 0 PUSHINT ADD DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS";
forall X -> int is_cell (X x) asm "<{ TRY:<{ CTOS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS";
forall X -> int is_slice (X x) asm "<{ TRY:<{ SBITS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS";
forall X -> int is_tuple (X x) asm "ISTUPLE";
forall X -> int cast_to_int (X x) asm "NOP";
forall X -> cell cast_to_cell (X x) asm "NOP";
forall X -> slice cast_to_slice (X x) asm "NOP";
forall X -> tuple cast_to_tuple (X x) asm "NOP";
forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP";
forall X -> () resolve_type (X value) impure {
;; değer burada X tipindedir, çünkü kesin değer nedir bilmiyoruz - değeri kontrol etmemiz ve sonra dönüştürmemiz gerekir
if (is_null(value)) {
;; null ile bir şeyler yap
}
elseif (is_int(value)) {
int valueAsInt = cast_to_int(value);
;; int ile bir şeyler yap
}
elseif (is_slice(value)) {
slice valueAsSlice = cast_to_slice(value);
;; slice ile bir şeyler yap
}
elseif (is_cell(value)) {
cell valueAsCell = cast_to_cell(value);
;; cell ile bir şeyler yap
}
elseif (is_tuple(value)) {
tuple valueAsTuple = cast_to_tuple(value);
;; tuple ile bir şeyler yap
}
}
() main () {
;; boş bir demet oluştur
tuple stack = empty_tuple();
;; diyelim ki bir demetimiz var ve bunların kesin tiplerini bilmiyoruz
stack~tpush("Bazı metinler");
stack~tpush(4);
;; değerimizin tipi ne olduğunu bilmediğimiz için var kullanıyoruz
var value = stack~tpop();
resolve_type(value);
}
💡 Yararlı bağlantılar
"TVM talimatları" belgelerinde
Geçerli zamanı nasıl alırsınız
int current_time = now();
if (current_time > 1672080143) {
;; bazı işlemleri yap
}
Rastgele sayı nasıl üretilir
Lütfen unutmayın ki bu rastgele sayı üretme yöntemi güvenli değildir.
Daha fazla bilgi için Rastgele Sayı Üretimi
sayfasına göz atın.
randomize_lt(); ;; bunu bir kez yap
int a = rand(10);
int b = rand(1000000);
int c = random();
Modül işlemleri
Örneğin, tüm 256 sayının aşağıdaki hesaplamasını çalıştırmak istediğimizi varsayalım: (xp + zp)*(xp-zp)
. Bu işlemlerin çoğunun kriptografi için kullanıldığını göz önünde bulundurursak, aşağıdaki örnekte montgomery eğrileri için modül operatörünü kullanıyoruz.
xp+zp geçerli bir değişken adı olduğunu unutmayın (boşluk yok).
(int) modulo_operations (int xp, int zp) {
;; 2^255 - 19 montgomery eğrileri için bir asal sayıdır, yani tüm işlemler bunun asal sayısı etrafında yapılmalıdır
int prime = 57896044618658097711785492504343953926634992332820282019728792003956564819949;
;; muldivmod bir sonraki iki satırı kendisi işleme alır
;; int xp+zp = (xp + zp) % prime;
;; int xp-zp = (xp - zp + prime) % prime;
(_, int xp+zp*xp-zp) = muldivmod(xp + zp, xp - zp, prime);
return xp+zp*xp-zp;
}
💡 Yararlı bağlantılar
"muldivmod" belgelerinde
Hataları nasıl atarsınız
int number = 198;
throw_if(35, number > 50); ;; hata yalnızca sayı 50'den büyükse tetiklenecektir
throw_unless(39, number == 198); ;; hata yalnızca sayı 198'e EŞİT DEĞİLSE tetiklenecektir
throw(36); ;; hata her koşulda tetiklenecektir
Standart tvm istisna kodları
Demetleri tersine çevirmek
Demet verileri bir yığın olarak depoladığından, bazen diğer uçtan verileri okumak için demeti tersine çevirmemiz gerekir.
forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP";
int tuple_length (tuple t) asm "TLEN";
forall X -> (tuple) to_tuple (X x) asm "NOP";
(tuple) reverse_tuple (tuple t1) {
tuple t2 = empty_tuple();
repeat (t1.tuple_length()) {
var value = t1~tpop();
t2~tpush(value);
}
return t2;
}
() main () {
tuple t = to_tuple([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
tuple reversed_t = reverse_tuple(t);
~dump(reversed_t); ;; [10 9 8 7 6 5 4 3 2 1]
}
💡 Yararlı bağlantılar
"tpush()" belgelerinde
Belirli bir indeksle bir öğeyi listeden nasıl kaldırırsınız
int tlen (tuple t) asm "TLEN";
(tuple, ()) remove_item (tuple old_tuple, int place) {
tuple new_tuple = empty_tuple();
int i = 0;
while (i < old_tuple.tlen()) {
int el = old_tuple.at(i);
if (i != place) {
new_tuple~tpush(el);
}
i += 1;
}
return (new_tuple, ());
}
() main () {
tuple numbers = empty_tuple();
numbers~tpush(19);
numbers~tpush(999);
numbers~tpush(54);
~dump(numbers); ;; [19 999 54]
numbers~remove_item(1);
~dump(numbers); ;; [19 54]
}
Dizi eşitliğini belirleme
İki farklı şekilde eşitliği belirleyebiliriz. Biri dilim hash'ine dayanmaktadır, diğeri ise SDEQ asm talimatını kullanarak.
int are_slices_equal_1? (slice a, slice b) {
return a.slice_hash() == b.slice_hash();
}
int are_slices_equal_2? (slice a, slice b) asm "SDEQ";
() main () {
slice a = "Bazı metinler";
slice b = "Bazı metinler";
~dump(are_slices_equal_1?(a, b)); ;; -1 = doğru
a = "Metin";
;; Doğru adrese dilim içinden geçerli adresi almak için literal 'a' kullanıyoruz
b = "EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"a;
~dump(are_slices_equal_2?(a, b)); ;; 0 = yanlış
}
💡 Yararlı bağlantılar
"slice_hash()" belgelerinde
"SDEQ" belgelerinde
Hücrelerin eşit olup olmadığını belirleme
Hücre eşitliğini, hash'lerine dayanarak kolayca belirleyebiliriz.
int are_cells_equal? (cell a, cell b) {
return a.cell_hash() == b.cell_hash();
}
() main () {
cell a = begin_cell()
.store_uint(123, 16)
.end_cell();
cell b = begin_cell()
.store_uint(123, 16)
.end_cell();
~dump(are_cells_equal?(a, b)); ;; -1 = doğru
}
💡 Yararlı bağlantılar
"cell_hash()" belgelerinde
Demetlerin eşit olup olmadığını belirleme
Daha gelişmiş bir örnek, her bir demet değerini yineleyip karşılaştırmaktır. X olduklarından, kontrol etmemiz ve karşılık gelen tipe dönüştürmemiz gerekiyor ve eğer demet ise yinelemeli olarak dönüştürmemiz gerekiyor.
int tuple_length (tuple t) asm "TLEN";
forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP";
forall X -> int cast_to_int (X x) asm "NOP";
forall X -> cell cast_to_cell (X x) asm "NOP";
forall X -> slice cast_to_slice (X x) asm "NOP";
forall X -> tuple cast_to_tuple (X x) asm "NOP";
forall X -> int is_null (X x) asm "ISNULL";
forall X -> int is_int (X x) asm "<{ TRY:<{ 0 PUSHINT ADD DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS";
forall X -> int is_cell (X x) asm "<{ TRY:<{ CTOS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS";
forall X -> int is_slice (X x) asm "<{ TRY:<{ SBITS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS";
forall X -> int is_tuple (X x) asm "ISTUPLE";
int are_slices_equal? (slice a, slice b) asm "SDEQ";
int are_cells_equal? (cell a, cell b) {
return a.cell_hash() == b.cell_hash();
}
(int) are_tuples_equal? (tuple t1, tuple t2) {
int equal? = -1; ;; başlangıç değeri doğru
if (t1.tuple_length() != t2.tuple_length()) {
;; demetler uzunluk açısından farklıysa eşit olamazlar
return 0;
}
int i = t1.tuple_length();
while (i > 0 & equal?) {
var v1 = t1~tpop();
var v2 = t2~tpop();
if (is_null(t1) & is_null(t2)) {
;; null her zaman eşittir
}
elseif (is_int(v1) & is_int(v2)) {
if (cast_to_int(v1) != cast_to_int(v2)) {
equal? = 0;
}
}
elseif (is_slice(v1) & is_slice(v2)) {
if (~ are_slices_equal?(cast_to_slice(v1), cast_to_slice(v2))) {
equal? = 0;
}
}
elseif (is_cell(v1) & is_cell(v2)) {
if (~ are_cells_equal?(cast_to_cell(v1), cast_to_cell(v2))) {
equal? = 0;
}
}
elseif (is_tuple(v1) & is_tuple(v2)) {
;; iç içe demetlerin eşitliğini yinelemeli olarak belirle
if (~ are_tuples_equal?(cast_to_tuple(v1), cast_to_tuple(v2))) {
equal? = 0;
}
}
else {
equal? = 0;
}
i -= 1;
}
return equal?;
}
() main () {
tuple t1 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]);
tuple t2 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]);
~dump(are_tuples_equal?(t1, t2)); ;; -1
}
💡 Yararlı bağlantılar
"cell_hash()" belgelerinde
"TVM talimatları" belgelerinde
İç adres nasıl üretilir
Sözleşmemiz yeni bir sözleşme dağıtmalıysa ama adresini bilmiyorsak iç bir adres üretmemiz gerekiyor. Diyelim ki zaten state_init
var - yeni sözleşmenin kodu ve verileri.
Mesaj AddressInt TLB için karşılık gelen bir iç adres oluşturur.
(slice) generate_internal_address (int workchain_id, cell state_init) {
;; addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt;
return begin_cell()
.store_uint(2, 2) ;; addr_std$10
.store_uint(0, 1) ;; herhangi bir mesaj yok
.store_int(workchain_id, 8) ;; işçilerin kimliği: -1
.store_uint(cell_hash(state_init), 256)
.end_cell().begin_parse();
}
() main () {
slice deploy_address = generate_internal_address(workchain(), state_init);
;; o zaman yeni sözleşmeyi dağıtabiliriz
}
💡 Not
Bu örnekte, işçi kimliğini almak için
workchain()
kullanıyoruz. İşçi kimliği ile ilgili daha fazla bilgi için belgelere göz atabilirsiniz.
💡 Yararlı bağlantılar
"cell_hash()" belgelerinde
Dış adres nasıl üretilir
Bu formatta bir adresi nasıl oluşturacağımızı anlamak için block.tlb dosyasından TL-B şemasını kullanıyoruz.
(int) ubitsize (int a) asm "UBITSIZE";
slice generate_external_address (int address) {
;; addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt;
int address_length = ubitsize(address);
return begin_cell()
.store_uint(1, 2) ;; addr_extern$01
.store_uint(address_length, 9)
.store_uint(address, address_length)
.end_cell().begin_parse();
}
Adresin kapladığı bit sayısını belirlememiz gerektiğinden, aşağıdaki gibi asm fonksiyonu
ile UBITSIZE
opcodeunu uma tanımlamak da gereklidir, bu da sayıyı saklamak için gereken minimum bit sayısını döndürecektir.
💡 Yararlı bağlantılar
"TVM talimatları" belgelerinde
Yerel depolamada sözlüğü nasıl saklar ve yükleriz
Sözlüğü yükleme mantığı
slice local_storage = get_data().begin_parse();
cell dictionary_cell = new_dict();
if (~ slice_empty?(local_storage)) {
dictionary_cell = local_storage~load_dict();
}
Sözlüğü saklama mantığı ise aşağıdaki örneğe benzemektedir:
set_data(begin_cell().store_dict(dictionary_cell).end_cell());
💡 Yararlı bağlantılar
"get_data()" belgelerinde
"new_dict()" belgelerinde
"slice_empty?()" belgelerinde
"load_dict()" belgelerinde
"~" belgelerinde
Basit bir mesaj nasıl gönderilir
Bir mesaj göndermenin alışıldık yolu aslında basit bir mesajdır. Mesajın gövdesinin yorum
olduğunu belirtmek için, mesaj metninden önce 32 bit
kısmını 0
olarak ayarlamalıyız.
cell msg = begin_cell()
.store_uint(0x18, 6) ;; bayraklar
.store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; hedef adres
.store_coins(100) ;; gönderilecek nanoTon miktarı
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; varsayılan mesaj başlıkları (mesaj gönderme sayfasına bak)
.store_uint(0, 32) ;; sıfır opcode - yorumla basit transfer mesajı anlamına gelir
.store_slice("FunC'tan Merhaba!") ;; yorum
.end_cell();
send_raw_message(msg, 3); ;; mod 3 - ücretleri ayrı öde, hataları görmezden gel
💡 Yararlı bağlantılar
"Mesaj yapısı" belgelerinde
Gelen hesapla mesaj nasıl gönderilir
Aşağıdaki sözleşme örneği, kullanıcı ve ana sözleşme arasında herhangi bir işlem yapmamız gerektiğinde, yani bir proxy sözleşmesine ihtiyaç duyduğumuzda bize yararlıdır.
() recv_internal (slice in_msg_body) {
{-
Bu, bir proxy sözleşmesinin basit bir örneğidir.
in_msg_body'nın mesaj modunu, gövdesini ve gönderilecek hedef adresini içermesini bekleyecektir.
-}
int mode = in_msg_body~load_uint(8); ;; ilk bayt msg modunu içerecektir
slice addr = in_msg_body~load_msg_addr(); ;; ardından hedef adresi ayrıştırırız
slice body = in_msg_body; ;; in_msg_body'da kalan her şey yeni mesajın gövdesi olacaktır
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_coins(100) ;; yalnızca örnek olması açısından
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; varsayılan mesaj başlıkları (gönderim sayfasına bak)
.store_slice(body)
.end_cell();
send_raw_message(msg, mode);
}
💡 Yararlı bağlantılar
"Mesaj yapısı" belgelerinde
"load_msg_addr()" belgelerinde
Tam bakiye ile mesaj nasıl gönderilir
Akıllı sözleşmenin tüm bakiyesini göndermemiz gerekiyorsa, bu durumda mod 128
kullanmamız gerekecek. Bu tür bir durumda, ödemeleri kabul eden ve ana sözleşmeye ileten bir proxy sözleşmesi olacaktır.
cell msg = begin_cell()
.store_uint(0x18, 6) ;; bayraklar
.store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; hedef adres
.store_coins(0) ;; şu anda bu değeri umursamıyoruz
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; varsayılan mesaj başlıkları (mesaj gönderme sayfasına bak)
.store_uint(0, 32) ;; sıfır opcode - yorumla basit transfer mesajı anlamına gelir
.store_slice("FunC'tan Merhaba!") ;; yorum
.end_cell();
send_raw_message(msg, 128); ;; mod = 128, mevcut akıllı sözleşmenin kalan tüm bakiyesini taşıyacak mesajlar için kullanılır
💡 Yararlı bağlantılar
"Mesaj yapısı" belgelerinde
"Mesaj modları" belgelerinde
Uzun metin yorumuyla nasıl mesaj gönderilir
Bildiğimiz gibi, bir cell
içerisine yalnızca 127 karakter sığabilir ( 📌 Faydalı bağlantılar
"Dahili mesajlar" belgelerinde
Sadece bir dilimden veri bitleri nasıl alınır (refs olmadan)
Eğer slice
içerisindeki refs
ile ilgilenmiyorsak, ayrı bir veri elde edip onunla çalışabiliriz.
slice s = begin_cell()
.store_slice("Bazı veri bitleri...")
.store_ref(begin_cell().end_cell()) ;; bazı referanslar
.store_ref(begin_cell().end_cell()) ;; bazı referanslar
.end_cell().begin_parse();
slice s_only_data = s.preload_bits(s.slice_bits());
📌 Faydalı bağlantılar
"Slice primitives" belgelerinde
"preload_bits()" belgelerinde
"slice_bits()" belgelerinde
Kendi değiştirme metodunuzu nasıl tanımlarsınız
Değiştirme yöntemleri, verilerin aynı değişken içinde değiştirilmesine olanak tanır. Bu, diğer programlama dillerindeki referanslama ile karşılaştırılabilir.
(slice, (int)) load_digit (slice s) {
int x = s~load_uint(8); ;; dilimden 8 bit (bir karakter) yükle
x -= 48; ;; karakter '0' kodu 48 olduğundan, sayıyı elde etmek için bunu çıkarıyoruz
return (s, (x)); ;; modifiye edilmiş dilimimizi ve yüklenmiş rakamı döndür
}
() main () {
slice s = "258";
int c1 = s~load_digit();
int c2 = s~load_digit();
int c3 = s~load_digit();
;; burada s "", c1 = 2, c2 = 5, c3 = 8'e eşit
}
📌 Faydalı bağlantılar
"Değiştirme yöntemleri" belgelerinde
Bir sayıyı n. kuvvetine nasıl yükseltiriz
;; Optimizasyonsuz variant
int pow (int a, int n) {
int i = 0;
int value = a;
while (i val çifti ile bir şey yap
(key, val, flag) = d.udict_get_next?(256, key);
}
📌 Faydalı bağlantılar
"Sözlükler primitives" belgelerinde
"dict_get_max?()" belgelerinde
"dict_get_min?()" belgelerinde
"dict_get_next?()" belgelerinde
"dict_set()" belgelerinde
Sözlüklerden bir değeri nasıl sileriz
cell names = new_dict();
names~udict_set(256, 27, "Alice");
names~udict_set(256, 25, "Bob");
names~udict_delete?(256, 27);
(slice val, int key) = names.udict_get?(256, 27);
~dump(val); ;; null() -> bu, anahtarın sözlükte bulunmadığı anlamına gelir
Cell ağaçlarını nasıl yineleyerek geçeriz
Bildiğimiz gibi, bir cell
, en fazla 1023 bit
veri ve 4 refs
depolayabilir. Bu sınırı aşmak için, bir hücreler ağacı kullanabiliriz; ancak bunu yapabilmek için düzgün veri işleme sağlamak üzere yineleyebilmemiz gerekir.
forall X -> int is_null (X x) asm "ISNULL";
forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS";
forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS";
() main () {
;; örnek olarak sadece bazı hücreler
cell c = begin_cell()
.store_uint(1, 16)
.store_ref(begin_cell()
.store_uint(2, 16)
.end_cell())
.store_ref(begin_cell()
.store_uint(3, 16)
.store_ref(begin_cell()
.store_uint(4, 16)
.end_cell())
.store_ref(begin_cell()
.store_uint(5, 16)
.end_cell())
.end_cell())
.end_cell();
;; hiçbir verisi olmayan bir tuple oluşturuyoruz, bu yığın rolünü oynar
tuple stack = null();
;; ana hücreyi işlemek için yığına götür
stack~push_back(c);
;; stack null olmadığı sürece bunu yap
while (~ stack.is_null()) {
;; yığından hücreyi al ve işlemek için bunu bir dilime dönüştür
slice s = stack~pop_back().begin_parse();
;; s verisi ile bir şey yap
;; eğer mevcut dilimin herhangi bir referansı varsa, bunları yığına ekle
repeat (s.slice_refs()) {
stack~push_back(s~load_ref());
}
}
}
📌 Faydalı bağlantılar
"Lisp tarzı listeler" belgelerinde
"null()" belgelerinde
"slice_refs()" belgelerinde
Lisp tarzı listelerde nasıl iterasyon yaparız
Tuple veri türü en fazla 255 değer tutabilir. Eğer bu yeterli değilse, o zaman bir lisp tarzı liste kullanmalıyız. Bir tuple'ı bir tuple'ın içine koyabiliriz, böylece sınırı aşarız.
forall X -> int is_null (X x) asm "ISNULL";
forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS";
forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS";
() main () {
;; bazı örnek listeler
tuple l = null();
l~push_back(1);
l~push_back(2);
l~push_back(3);
;; elemanlar arasında iterasyon yapıyoruz
;; lüzum açısından bu iterasyon ters sırada
while (~ l.is_null()) {
var x = l~pop_back();
;; x ile bir şey yap
}
}
📌 Faydalı bağlantılar
"Lisp tarzı listeler" belgelerinde
"null()" belgelerinde
Deploy mesajı nasıl gönderilir (sadece stateInit ile, stateInit ve body ile)
() deploy_with_stateinit(cell message_header, cell state_init) impure {
var msg = begin_cell()
.store_slice(begin_parse(msg_header))
.store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit))
.store_uint(0, 1) ;; body:(Either X ^X)
.store_ref(state_init)
.end_cell();
;; mod 64 - yeni mesajda kalan değeri taşımak
send_raw_message(msg, 64);
}
() deploy_with_stateinit_body(cell message_header, cell state_init, cell body) impure {
var msg = begin_cell()
.store_slice(begin_parse(msg_header))
.store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit))
.store_uint(1, 1) ;; body:(Either X ^X)
.store_ref(state_init)
.store_ref(body)
.end_cell();
;; mod 64 - yeni mesajda kalan değeri taşımak
send_raw_message(msg, 64);
}
stateInit hücresi nasıl oluşturulur
() build_stateinit(cell init_code, cell init_data) {
var state_init = begin_cell()
.store_uint(0, 1) ;; split_depth:(Maybe (## 5))
.store_uint(0, 1) ;; special:(Maybe TickTock)
.store_uint(1, 1) ;; (Maybe ^Cell)
.store_uint(1, 1) ;; (Maybe ^Cell)
.store_uint(0, 1) ;; (HashmapE 256 SimpleLib)
.store_ref(init_code)
.store_ref(init_data)
.end_cell();
}
Sözleşme adresi nasıl hesaplanır (stateInit kullanarak)
() calc_address(cell state_init) {
var future_address = begin_cell()
.store_uint(2, 2) ;; addr_std$10
.store_uint(0, 1) ;; anycast:(Maybe Anycast)
.store_uint(0, 8) ;; workchain_id:int8
.store_uint(cell_hash(state_init), 256) ;; address:bits256
.end_cell();
}
Akıllı sözleşme mantığı nasıl güncellenir
Aşağıda, sayacı artırma ve akıllı sözleşme mantığını güncelleme işlevine sahip basit bir CounterV1
akıllı sözleşme örneği bulunmaktadır.
() recv_internal (slice in_msg_body) {
int op = in_msg_body~load_uint(32);
if (op == op::increase) {
int increase_by = in_msg_body~load_uint(32);
ctx_counter += increase_by;
save_data();
return ();
}
if (op == op::upgrade) {
cell code = in_msg_body~load_ref();
set_code(code);
return ();
}
}
📌 Not: Akıllı sözleşme üzerinde işlem yaptıktan sonra, metreküp azaltma özelliğinin eksik olduğunu fark edersiniz. Akıllı sözleşmenin
CounterV1
kodunu kopyalayıpincrease
işlevinin yanına yeni birdecrease
işlevi eklemeniz gerekir. Artık kodunuz şu şekilde görünmektedir:
() recv_internal (slice in_msg_body) {
int op = in_msg_body~load_uint(32);
if (op == op::increase) {
int increase_by = in_msg_body~load_uint(32);
ctx_counter += increase_by;
save_data();
return ();
}
if (op == op::decrease) {
int decrease_by = in_msg_body~load_uint(32);
ctx_counter -= decrease_by; ;; burada doğru değişken kullanıldı
save_data();
return ();
}
if (op == op::upgrade) {
cell code = in_msg_body~load_ref();
set_code(code);
return ();
}
}
CounterV2
akıllı sözleşmesi hazır olduktan sonra, onu off-chain olarak bir cell
içerisine derlemeli ve CounterV1
akıllı sözleşmesine bir güncelleme mesajı göndermelisiniz.
await contractV1.sendUpgrade(provider.sender(), {
code: await compile('ContractV2'),
value: toNano('0.05'),
});
📌 Faydalı bağlantılar
Var olan bir adrese kod yeniden dağıtmak mümkün mü yoksa bu yeni bir sözleşme olarak mı dağıtılmalıdır?
"set_code()" belgelerinde